using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Spline3D : IEnumerable<Vector3[]> {
	#region Properties
	//The path that the track follows
	private CubicSpline[] _SplineSections;
	
	//Information for the outside world.
	public Vector3 startPoint
	{
		get
		{
			return _SplineSections[0].points[0];
		}
	}
	
	public Vector3 endPoint
	{
		get
		{
			return _SplineSections[_SplineSections.Length-1].points[1];
		}
	}
	
	public Vector3 center
	{
		get
		{
			return (startPoint + endPoint)/2;
		}
	}
	
	public Vector3 direction
	{
		get
		{
			return endPoint - startPoint;
		}
	}
	
	public SplineControlPoint startSplineControlPoint
	{
		get
		{
			return new SplineControlPoint(startPoint, startPoint-_SplineSections[0].points[1], new float[] {1, 1});
		}
	}
			
	public SplineControlPoint endSplineControlPoint
	{
		get
		{
			return new SplineControlPoint(endPoint, endPoint-_SplineSections[_SplineSections.Length-1].points[0], new float[] {1, 1});
		}
	}
				
	public Transform type {get; set;}
	public int layer {get; set;}
	#endregion
	
	#region Constructors
	//Public constructor
	public Spline3D(SplineControlPoint[] cp)
	{
		_SplineSections = new CubicSpline[cp.Length-1];
		
		//Starting at points 0 and 1, generate a CubicSpline from point 0 to 1, 
		//  then from 1 to 2 et cetera.
		for (int i = 1; i < cp.Length; i++)
		{
			_SplineSections[i-1] = new CubicSpline(cp[i-1], cp[i]);
		}
		layer = LayerMask.NameToLayer("Track");
	}
	
	//Private constructor used in the Rasterize method
	private Spline3D(CubicSpline ss)
	{
		_SplineSections = new CubicSpline[] {ss};
	}
	#endregion
	
	#region Subdivision functions
	//Subdivides the spline into smaller splines
	private CubicSpline[] Subdivide(int lmax)
	{
		List<CubicSpline> accu = new List<CubicSpline>();
		foreach(CubicSpline spline in _SplineSections)
		{
			accu.AddRange(spline.subdivide(lmax));
		}
		
		return accu.ToArray();
	}
	
	//Subdivides the spline into smaller splines and returns them as an array of Spline3Ds
	public Spline3D[] Rasterize(int lmax)
	{
		CubicSpline[] SubdividedSplines = Subdivide(lmax);
		Spline3D[] RasterizedSpline3Ds = new Spline3D[SubdividedSplines.Length];
		
		for(int i = 0; i < RasterizedSpline3Ds.Length; i++)
		{
			RasterizedSpline3Ds[i] = new Spline3D(SubdividedSplines[i]);
		}
		
		return RasterizedSpline3Ds;
	}
	
	//Smooths the spline by subdividing it's internal spline sections
	//untill they're shorter than the specified length
	public void Smooth(int lmax)
	{
		_SplineSections = Subdivide(lmax);
	}
	#endregion
	
	#region Enumerator functions
	//Implements an enumeable interface for ease of access.
	public IEnumerator<Vector3[]> GetEnumerator()
	{
		return new Spline3DEnumerator(_SplineSections);
	}
	
	IEnumerator IEnumerable.GetEnumerator()
	{
		return GetEnumerator();
	}
	#endregion
}

class Spline3DEnumerator : IEnumerator<Vector3[]> {
    private CubicSpline[]  _splines;
    private int _index;

    public Spline3DEnumerator(CubicSpline[] s)
    {
        _splines = s;
        _index = -1;
    }

    public void Reset()

    {
        _index = -1;
    }

    public object Current
    {
        get
        {
            return _splines[_index].points;
        }
    }
	
	Vector3[] IEnumerator<Vector3[]>.Current 
    {
        get
        {
            return _splines[_index].points;
        }
    }

    public bool MoveNext()
    {
        _index++;
        if (_index >= _splines.Length)
            return false;
        else
            return true;
    }
	
	public void Dispose()
	{
		
	}
}